package org.sosie2.service.authentication;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;

import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.ws.security.WSSecurityEngine;
import org.apache.ws.security.WSSecurityEngineResult;
import org.apache.ws.security.WSUsernameTokenPrincipal;
import org.opensaml.SAMLAttribute;
import org.opensaml.SAMLException;
import org.sosie2.service.framework.AbstractService;
import org.sosie2.service.framework.SAMLAnnotation;
import org.sosie2.service.framework.SosieException;
import org.sosie2.service.framework.Utils;
import org.w3c.dom.Document;


/**
 * 
 * The service checks whether the client is or not registered in the LDAP directory.
 * The result is embedded into the SOAP header message sent as a response, in the form
 * of an annotation.
 * @author Gabriela
 */

public class LDAPAuthenticationService extends AbstractService
{
	static Logger logger 					= Logger.getLogger(LDAPAuthenticationService.class);
	
	private static final String ACTOR  		= Utils.AUTH_ACTOR;
	private static final String AUTHOR 		= Utils.AUTH_AUTHOR;
	
	private CallbackHandler callback 		= null;
	
	protected String authenticatedUser 		= null;
	
	/**
	 * Initializes the logger config file, and the callback handler which will be 
	 * used when checking user passwords 
	 */
	public void init() {
		
		Utils instanceUtils = Utils.get();
		URL url = instanceUtils.getFile("configuration//" + Utils.LOGGER_FILE);
		if (url == null)
			System.out.println("---------------------URL NULL" );
		else 	
			PropertyConfigurator.configure(url);
		
		logger.info("Authentication service started ...");
		
		try {
			final ClassLoader cl = Thread.currentThread().getContextClassLoader();
			Class c = cl.loadClass("org.sosie2.service.common.LDAPCallbackHandler");
			callback = (CallbackHandler) c.newInstance();
		} catch(Exception ex) {
			logger.fatal("Exception in Authentication Service");
			ex.printStackTrace();
		}
	}
	
	/**
	 * simple constructor
	 */
	public LDAPAuthenticationService() {
		init();
	}
	
	/**
	 * This is the main method of the web service. It parses the header of the soap message
	 * and after getting the username, it passes it to the callback handler
	 * to check it against the given password;
	 * the callback uses calls to a method that interrogates a LDAP server 
	 */
	public Message invoke(Message req, Message resp) throws Exception {
		// initialization of logger and callback handler
		logger.info("start");
		
		// verify the username and password with the callback from wss4j 
		WSSecurityEngine secEngine = WSSecurityEngine.getInstance();		
		java.util.Vector results = secEngine.processSecurityHeader(req.getSOAPEnvelope().getAsDocument(),null,callback, null);		
		
		if (results.size() == 1) {
			WSSecurityEngineResult eResult = (WSSecurityEngineResult)results.get(0);
			WSUsernameTokenPrincipal usernameTokenPrincipal = (WSUsernameTokenPrincipal)eResult.getPrincipal();
			
			String wannabeName = usernameTokenPrincipal.getName();
    		
			logger.info("User " + wannabeName + "is authenticated !");
			this.authenticatedUser = wannabeName;
			
			// the response sent to the client, is composed of its original message + an annotation;
			// first, copy the headers of the request message, into the response message
    		SOAPHeader header = req.getSOAPHeader();
    		this.copyHeader(header, resp.getSOAPHeader());
    		
    		SOAPBody respBody = resp.getSOAPBody();
    		SOAPBody reqBody = req.getSOAPBody();
    		this.copyBody(reqBody, respBody);
       		
    		Document doc = resp.getSOAPEnvelope().getAsDocument();
    		
    		// create attribute-values array
    		String[][] att = new String[1][2];
    		att[0][0] = "authenticatedUser";
    		att[0][1] = wannabeName;
    		
    		//attach the hashmap + rest of parameters to the annotation
    		SAMLAnnotation sa = new SAMLAnnotation (att, ACTOR, "LDAPAuthentication");
    		doc = addAnnotation(doc, sa, AUTHOR);
    		
    		SOAPMessage soapMsg = Utils.toMessage(doc);
    		ByteArrayOutputStream out = new ByteArrayOutputStream();
    		soapMsg.writeTo(out);
    		
    		Message msg = new Message(new ByteArrayInputStream(out.toByteArray()), false);
    	    return msg;
		} else {
			logger.info("User is invalid. Alarm !" );
			throw new SosieException("Authentication failed. Invalid user!");
		}	
    		
    }
}

